//
// Created by harrybao on 2019/3/27.
//

#ifndef CMAKE_VECTOR3_H
#define CMAKE_VECTOR3_H
#include <cstdint>
#include <cmath>
#include <iosfwd>
#include "math_define.h"

class Vector3;
std::ostream &operator<<(std::ostream &stream, const Vector3 &pos);

class Vector3
{
public:
    Vector3();
    Vector3(float x, float y, float z, uint32_t layer = 0, uint8_t view_layer = 0);
    Vector3(const Vector3 &v);

    inline float x() const
    {
        return x_;
    }
    inline float y() const
    {
        return y_;
    }
    inline float z() const
    {
        return z_;
    }
    inline uint32_t layer() const
    {
        return layer_;
    }
    inline uint8_t view_layer() const
    {
        return view_layer_;
    }
    inline void x(float value)
    {
        x_ = value;
    }
    inline void y(float value)
    {
        y_ = value;
    }
    inline void z(float value)
    {
        z_ = value;
    }
    inline void layer(uint32_t value)
    {
        layer_ = value;
    }
    inline void view_layer(uint8_t value)
    {
        view_layer_ = value;
    }
    inline void Set(float x, float y, float z, uint32_t layer = 0, uint8_t view_layer = 0)
    {
        x_ = x;
        y_ = y;
        z_ = z;
        layer_ = layer;
        view_layer_ = view_layer;
    }
    inline void SetX(float x)
    {
        x_ = x;
    }
    inline void SetY(float y)
    {
        y_ = y;
    }
    inline void SetZ(float z)
    {
        z_ = z;
    }
    inline void SetLayer(uint32_t layer)
    {
        layer_ = layer;
    }
    inline void SetViewLayer(uint8_t view_layer)
    {
        view_layer_ = view_layer;
    }
    inline Vector3 operator-() const
    {
        return Vector3(-x(), -y(), -z());
    }
    inline void operator=(const Vector3 &v)
    {
        x_ = v.x();
        y_ = v.y();
        z_ = v.z();
        layer_ = v.layer();
        view_layer_ = v.view_layer();
    }
    inline Vector3 operator-(const Vector3 &v) const
    {
        return Vector3(x_ - v.x(), y_ - v.y(), z_ - v.z(), layer_, view_layer_);
    }
    inline Vector3 operator+(const Vector3 &v) const
    {
        return Vector3(x_ + v.x(), y_ + v.y(), z_ + v.z(), layer_, view_layer_);
    }
    inline Vector3 operator*(const Vector3 &v) const
    {
        return Vector3(x_ * v.x(), y_ * v.y(), z_ * v.z(), layer_, view_layer_);
    }
    inline Vector3 operator*(float d) const
    {
        return Vector3(x_ * d, y_ * d, z_ * d, layer_, view_layer_);
    }
    inline Vector3 operator/(float d) const
    {
        return Vector3(x_ / d, y_ / d, z_ / d, layer_, view_layer_);
    }
    inline bool operator==(const Vector3 &v) const
    {
        return (fabs(x_ - v.x()) < EPSINON) && (fabs(y_ - v.y()) < EPSINON) && (fabs(z_ - v.z()) < EPSINON) && layer_ == v.layer() && view_layer_ == v.view_layer();
    }
    inline bool operator!=(const Vector3 &v) const
    {
        return (x_ != v.x()) || (y_ != v.y()) || (z_ != v.z()) || (layer_ != v.layer() || (view_layer_ != v.view_layer()));
    }
    inline Vector3 &operator+=(const Vector3 &v)
    {
        x_ += v.x();
        y_ += v.y();
        z_ += v.z();
        return *this;
    }
    inline Vector3 &operator-=(const Vector3 &v)
    {
        x_ -= v.x();
        y_ -= v.y();
        z_ -= v.z();
        return *this;
    }

    inline Vector3 &operator*=(const Vector3 &v)
    {
        x_ *= v.x();
        y_ *= v.y();
        z_ *= v.z();
        return *this;
    }

    inline Vector3 &operator*=(float d)
    {
        x_ *= d;
        y_ *= d;
        z_ *= d;
        return *this;
    }

    inline Vector3 &operator/=(float d)
    {
        x_ /= d;
        y_ /= d;
        z_ /= d;
        return *this;
    }
    // Out Protected float_array
    inline void VectorToFloat(float *float_array) const
    {
        float_array[0] = x_;
        float_array[1] = y_;
        float_array[2] = z_;
    }
    void HorizontalRotate(float degree, bool normalized = false);

    //Returns the angle in degrees between v and u.
    static float Angle(const Vector3 &v, const Vector3 &u);
    //Returns a copy of vector3 with its magnitude clamped to maxLength.
    static Vector3 ClampMagnitude(const Vector3 &v, float maxLength);
    static Vector3 Cross(const Vector3 &v, const Vector3 &u);
    static float Distance(const Vector3 &v, const Vector3 &u);
    static float HorizontaledDistance(const Vector3 &v, const Vector3 &u);
    static float Dot(const Vector3 &v, const Vector3 &u);
    static float Magnitude(const Vector3 &v);
    static float sqrtMagnitude(const Vector3 &v);
    static Vector3 Normalized(const Vector3 &v);
    static Vector3 Horizontaled(const Vector3 &v);
    //Rotate around Y-axis ((0, 0) for plane) with degree (clockwise positive)
    static Vector3 HorizontalRotate(const Vector3 &v, float degree, bool normalized = false);
    /*
    * whether vec-r is in the clockwise direction of vec-f in x-z plane.
    */
    static inline bool Clockwise(const Vector3 &fiduciary, const Vector3 &relativity)
    {
        return (fiduciary.z() * relativity.x() - fiduciary.x() * relativity.z()) > 0;
    }

    static inline float Dot(float v1, float u1, float v2, float u2)
    {
        return v1 * u2 - v2 * u1;
    }

    static float inline AngleToFloat(const Vector3 &dir)
    {
        float face = Vector3::Angle(Vector3::forward, dir);
        return Vector3::Clockwise(Vector3::forward, dir) ? face : -face;
    }

    static Vector3 inline FloatToAngle(float angle)
    {
        return HorizontalRotate(Vector3::forward, angle);
    }

    static bool SameLayer(const Vector3 &v, const Vector3 &u)
    {
        return v.layer_ == u.layer_;
    }
    static bool SameViewLayer(const Vector3 &v, const Vector3 &u)
    {
        return v.view_layer_ == u.view_layer_;
    }

    void Normalize();
    inline void Zero()
    {
        x_ = 0;
        y_ = 0;
        z_ = 0;
    }
    inline bool IsZero() const { return ROIsZeroNumber(x_) && ROIsZeroNumber(y_) && ROIsZeroNumber(z_); }
    inline bool IsRealZero() const { return ROIsZeroNumber(x_) && ROIsZeroNumber(y_) && ROIsZeroNumber(z_) && IsDefaultLayer() && IsDefaultViewLayer(); }
    inline bool IsDefaultLayer() const { return layer_ == 0; }
    inline bool IsDefaultViewLayer() const { return view_layer_ == 0; }

    /*
    * vector maybe zero after Horizontal
    */
    inline void Horizontal()
    {
        y_ = 0;
        Normalize();
    }

    const std::string ToString() const;

    static const Vector3 back;
    static const Vector3 down;
    static const Vector3 forward;
    static const Vector3 up;
    static const Vector3 left;
    static const Vector3 right;
    static const Vector3 one;
    static const Vector3 zero;

private:
    float x_;
    float y_;
    float z_;
    uint32_t layer_;     // 战斗层
    uint8_t view_layer_; // 视野层
};
#endif //MATH_TEST_VECTOR3_H
